/*
 * =====================================================================================
 *
 *       Filename:  upload_stream_request.cc
 *
 *    Description:  
 *
 *        Version:  1.0
 *        Created:  2013年06月19日 14时25分49秒
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Xie Yanping (), 
 *        Company:  www.Qihoo.net
 *
 * =====================================================================================
 */

#include <time.h>
#include <sstream>
#include <iomanip>
#include <openssl/md5.h>
#include <boost/format.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/regex.hpp>
#include "redis_executor/redis_executor.h"
#include "redis_executor/redis_locator.h"
#include "redis_executor/scoped_reply.h"
#include "upload_stream_request.h"
#include "file_trans_redis.h"
#include "base/logging.h"

namespace imserver {
namespace fcgi {

std::string CalculateFileIdentify(const FileContent& file_content) {
  time_t now;
  time(&now);
  struct tm *timenow = localtime(&now);
  std::string str_time = boost::str(boost::format("%1%-%2%-%3%")
      % file_content.name
      % file_content.content_length
      % asctime(timenow));
  unsigned char digest[MD5_DIGEST_LENGTH];
  MD5((unsigned char*)str_time.c_str(),
      str_time.size(),
      (unsigned char*)&digest);
  std::stringstream res;

  for (size_t i = 0; i < MD5_DIGEST_LENGTH; ++i) {
    res << std::hex << std::setw(2)
      << std::setfill('0') << (unsigned int)digest[i];
  }

  LOG_DEBUG("cacl file identify result=" << res.str());
  return res.str();
}

std::string ParseValue(const std::string& line, const std::string& key) {
  std::string result, temp;
  std::string::size_type pos;

  if (!line.empty() && (line[0] == 'C')) {
    LOG_DEBUG("current line : " << line);
    pos = line.find(key);
    if (std::string::npos != pos) {
      temp = line.substr(pos + key.size());
      temp = temp.substr(temp.find_first_not_of(":=\" "));
      result = temp.substr(0, temp.find('"'));
    }
    LOG_DEBUG("key " << key << " = " << result);
  }

  return result;
}

extern std::string g_file_exts;

std::string BaseFilename(const std::string & path) {
  std::string::size_type pos = path.find_last_of("\\/");
  std::string name = path;
  if (std::string::npos != pos) name = path.substr(pos + 1);
  std::string ext = name.substr(name.find_last_of('.'));
  boost::to_lower(ext);
  if (std::string::npos != g_file_exts.find(ext)) name.append(".重命名");
  return name;
}

void ParseFileContent(FileContent& content,
                      std::string& body,
                      std::string& callback,
                      const std::string& spliter) {
  boost::regex reg(spliter);
  boost::sregex_token_iterator it(body.begin(), body.end(), reg, -1);
  boost::sregex_token_iterator end;
  std::string line, tmp, new_body, part;

  for (; it!=end; ++it) {
    part = *it;
    if (part.empty()) continue;

    std::stringstream stream(part);
    while (getline(stream, line)) {
      tmp = ParseValue(line, "name");
      if (tmp == "userfile") {
        content.name = BaseFilename(ParseValue(line, "filename"));
        getline(stream, line);
        content.content_type = ParseValue(line, "Content-Type");
        getline(stream, line);
        new_body = part.substr(stream.tellg());
        boost::trim(new_body);
        content.content_length = new_body.size();
        break;
      } else if (tmp == "callback") {
        getline(stream, line);
        callback = part.substr(stream.tellg());
        boost::trim(callback);
        break;
      }
    }
  }

  body = new_body;
}

bool FileTransUpstreamRequest::Response() {
  LOG_DEBUG("[FileTransUpstreamRequest::Response] called.");
  if (!fcgi_out_) {
    LOG_ERROR("Fast-cgi for upload stream of file transfer is NULL.");
    return false;
  }

  //get boundary
  std::string boundary;
  std::string pattern = "boundary=";
  std::string content_type = GetParam("CONTENT_TYPE");
  std::string::size_type p = content_type.find(pattern);
  if (p != std::string::npos) {
    boundary = content_type.substr(p + pattern.size());
    boundary = "--" + boundary;
  }
  LOG_DEBUG("boundary=" << boundary);

  // read line until get the content-type and content-length
  FileContent content;
  std::string callback;
  ParseFileContent(content, body_, callback, boundary);
  if (content.content_length > 1024 * 1024 * 10) {
    std::string rsp_header(
        "HTTP/1.1 400 Bad Request\r\n"
        "Content-type: text/html\r\n"
        "Content-Length: 0\r\n\r\n");
    FCGX_PutS(rsp_header.c_str(), fcgi_out_);
    return false;
  }

  std::string rsp_header("Content-type: text/html; charset=utf-8\r\n\r\n");
  FCGX_PutS(rsp_header.c_str(), fcgi_out_);

  // file content
  content.timestamp      = time(NULL);
  content.identify       = CalculateFileIdentify(content);
  boost::regex reg("^[\\w\\.]+$");
  boost::cmatch what;

  if (boost::regex_match(callback.c_str(), what, reg)) {
    boost::format fmt(
        "<html><body><script>"
        "document.domain=\"360.cn\";"
        " %1%({errno:%2%,name:\'%3%\',src:\'http://f.im.bang.360.cn/ft/d?fid=%4%\'})"
        "</script></body></html>");
    fmt % callback;

    for (int i = 0; i < 3; ++i) {
      if (0 == FileTransRedis::GetInst().SetFileContent(content) &&
          0 == FileTransRedis::GetInst().SetFileStream(body_, content.identify)) {
        LOG_INFO("SetFile succeed.(file=" << content.name
            << " length=" << content.content_length
            << " type=" << content.content_type
            << " identify=" << content.identify << ")");
        fmt % 0 % content.name % content.identify;
        FCGX_PutS(fmt.str().c_str(), fcgi_out_);
        return true;
      } else {
        LOG_WARN("SetFile retry - " << i << ".(file=" << content.name << ")");
      }
    }

    LOG_ERROR("SetFile failed.(file=" << content.name
        << " length=" << content.content_length
        << " type=" << content.content_type
        << " identify=" << content.identify << ")");
    fmt % 1 % content.name % content.identify;
    FCGX_PutS(fmt.str().c_str(), fcgi_out_);
    return false;
  }

  return true;
}

}
}
